package com.zwb.service.common;

import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 头发又黑又长
 * @Date 2022/8/23 16:18
 * @email zwb15083976291@163.com
 */
@Component
public class JwtTokenUtil {
    private static final String CLAIM_KEY_USERNAME = "sub";
    private static final String CLAIM_KEY_CREATE = "created";


    /**
     * token 枚举状态
     */

    public enum TokenStatus {
        SUCCESS("ok"),
        UNSUPPORTED("不支持的token"),
        MALFORMED("无效的token"),
        SIGNATURE("签名无效"),
        EXPIRED("token已过期"),
        ILLEGALARGUMENT("token为null"),
        ERROR("token 错误");

        public String message;

        TokenStatus(String message) {
            this.message = message;
        }

        public String getMessage() {
            return message;
        }




        @Override
        public String toString() {
            return "TokenStatus{" +
                    "message='" + message + '\'' +
                    '}';
        }
    }


    // 盐值
    @Value("${jwt.secret}")
    private String secret;
    // 失效时间
    @Value("${jwt.expiration}")
    private Long expiration;

    /**
     * 根据用户获取 token
     *
     * @param userDetails
     * @return
     */
    public String generatorToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        // 添加负载
        claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
        claims.put(CLAIM_KEY_CREATE, new Date());
        return generatorToken(claims);
    }


    /**
     * 根据map生成token
     *
     * @param claims
     * @return
     */
    public String generatorToken(Map<String, Object> claims) {
        return Jwts.builder().setClaims(claims)
                .setExpiration(generatorExpiration()) // 设置过期时间
                .signWith(SignatureAlgorithm.HS512, secret) // 签名
                .compact();
    }

    /**
     * 生产token失效时间
     *
     * @return
     */
    private Date generatorExpiration() {
        return new Date(System.currentTimeMillis() + expiration * 1000);
    }

    /**
     * 获取当前用户
     *
     * @param token
     * @return
     */
    public String getUserName(String token) {
        Claims claims = getClaimsForToken(token);
        return claims.getSubject();
    }

    /**
     * 从token 中获取 负载
     *
     * @param token
     * @return
     */
    public Claims getClaimsForToken(String token) {
        return Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token).getBody();

    }

    /**
     * 判断token 是否是当前用户
     */
    public boolean isTokenCurrentUser(String token, UserDetails userDetails) {
        String username = getUserName(token);
        return userDetails.getUsername().equals(username) && TokenStatus.SUCCESS == validateToken(token);
    }

    /**
     * 判断token是否有效
     * <p>
     * 返回空代表有效
     * 返回字符串表示错误的token
     *
     * @param token
     * @return
     */
    public TokenStatus validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
        } catch (UnsupportedJwtException e) {
            return TokenStatus.UNSUPPORTED;
        } catch (MalformedJwtException e) {
            return TokenStatus.MALFORMED;
        } catch (SignatureException e) {
            return TokenStatus.SIGNATURE;
        } catch (ExpiredJwtException e) {
            return TokenStatus.EXPIRED;
        } catch (IllegalArgumentException e) {
            return TokenStatus.ILLEGALARGUMENT;
        } catch (Exception e) {
            return TokenStatus.ERROR;
        }
        return TokenStatus.SUCCESS;

    }

    /**
     * token Token 是否已经过期了
     *
     * @param token
     * @return
     */
    public boolean isTokenExpired(String token) {
        return TokenStatus.EXPIRED == validateToken(token);
    }


    /**
     * 刷新token
     */
    public String RefreshToken(String token) {
        Claims claimsForToken = getClaimsForToken(token);
        claimsForToken.put(CLAIM_KEY_CREATE, new Date());
        return generatorToken(claimsForToken);
    }

}
